#include "Common.h"
#include "SystemV/AppLibSystemV_Base.hpp"

#include <stdarg.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ipc.h>

#include <sys/msg.h>
#include <sys/sem.h>
#include <sys/shm.h>

#include <fcntl.h>

union semun 
{
    int              val;            /* Value for SETVAL */
    struct semid_ds *buf;            /* Buffer for IPC_STAT, IPC_SET */
    unsigned short  *array;          /* Array for GETALL, SETALL */
    struct seminfo  *__buf;          /* Buffer for IPC_INFO (Linux-specific) */
};

ULONG AppLibSystemV_Ftok(IN CHAR *pcFile, IN SINT32 iProID, INOUT key_t *key)
{
    SINT32 iFd = -1;

    if (access(pcFile, F_OK) != 0)
    {
        iFd = open(pcFile, O_RDONLY | O_CREAT, 0777);
        if (iFd < 0)
        {
            LOG_ERROR("err %d [%s]\n", errno, strerror(errno));
            return STAT_ERR_FILE_OPEN;
        }
    }

    *key = ftok(pcFile, iProID);
    if ((*key) < 0)
    {
        LOG_ERROR("err %d [%s]\n", errno, strerror(errno));
        return STAT_ERR_FTOK;
    }

    return STAT_SUCCESS;
}

/**********************************************************
 * Function Name:        libSystem_V_Get
 * Description:          消息
 * Parameters[in]:       IN LIB_SYSTEM_V_TYPE_e type
 * Parameters[out]:
 * ReturnValue:          ULONG
 * Author:               liuke
 * Date:                 2024.04.16
 * Note:
 *     1. LIB_SYSTEM_V_TYPE_MSG
 *        libSystem_V_Get(type, <char *>pcFile);
 *     2. LIB_SYSTEM_V_TYPE_SHM
 *        libSystem_V_Get(type, <char *>pcFile, <size_t *>size);
 *     3. LIB_SYSTEM_V_TYPE_SEM
 *        libSystem_V_Get(type, <char *>pcFile, <int *>nsems);
***********************************************************/
SINT32 AppLibSystemV_Get(APP_LIB_SYSTEM_V_TYPE_e type, ...)
{
    va_list av;
    key_t key;
    CHAR *pcFile = NULL;
    SINT32 iSysID = STAT_SUCCESS;
    ULONG uRet = STAT_SUCCESS;

    va_start(av, type);

    pcFile = va_arg(av, CHAR *);

    uRet = AppLibSystemV_Ftok(pcFile, APP_LIB_SYSTEM_V_PROJECT_ID, &key);
    if (uRet != STAT_SUCCESS)
    {
        va_end(av);
        return -1;
    }

    switch (type)
    {
        case APP_LIB_SYSTEM_V_TYPE_MSG:
            {
                iSysID = msgget(key, IPC_CREAT | 0666);
            }
            break;

        case APP_LIB_SYSTEM_V_TYPE_SHM:
            {
                size_t size = va_arg(av, size_t);
                iSysID = shmget(key, size, IPC_CREAT | 0666);
            }
            break;

        case APP_LIB_SYSTEM_V_TYPE_SEM:
            {
                SINT32 nsems = va_arg(av, SINT32);
                iSysID = semget(key, nsems, IPC_CREAT | 0666);
            }
            break;

        default:
            break;
    }

    va_end(av);

    return iSysID;
}

/**********************************************************
 * Function Name:        LibSystem_V_Ctl
 * Description:          消息
 * Parameters[in]:       IN LIB_SYSTEM_V_TYPE_e type
 * Parameters[out]:
 * ReturnValue:          ULONG
 * Author:               liuke
 * Date:                 2024.04.16
 * Note:
 *     1. APP_LIB_SYSTEM_V_TYPE_MSG
 *        AppLibSystem_V_Ctl(type, iSysID, <SINT32>cmd, <struct msqid_ds *>buf);
 *     2. APP_LIB_SYSTEM_V_TYPE_SHM
 *        AppLibSystem_V_Ctl(type, iSysID, <SINT32>cmd, <struct shmid_ds *>buf);
 *     3. APP_LIB_SYSTEM_V_TYPE_SEM
 *        AppLibSystem_V_Ctl(type, iSysID, SINT32 semnum, <SINT32>cmd, ...);
***********************************************************/
ULONG AppLibSystemV_Ctl(APP_LIB_SYSTEM_V_TYPE_e type, ...)
{
    SINT32 iReturn = STAT_SUCCESS;
    SINT32 iSysID = -1;
    va_list av;

    va_start(av, type);
    iSysID = va_arg(av, int);

    switch (type)
    {
        case APP_LIB_SYSTEM_V_TYPE_MSG:
            {
                SINT32 cmd = va_arg(av, SINT32);
                struct msqid_ds *buf = va_arg(av, struct msqid_ds *);
                iReturn = msgctl(iSysID, cmd, buf);
                if (iReturn < 0)
                {
                    LOG_ERROR("msgctl cmd: %d, err: %s\n", cmd, strerror(errno));
                    va_end(av);
                    return STAT_ERR_SYSTEM_V_MSG_CTRL;
                }
            }
            break;

        case APP_LIB_SYSTEM_V_TYPE_SHM:
            {
                SINT32 cmd = va_arg(av, SINT32);
                struct shmid_ds *buf = va_arg(av, struct shmid_ds *);
                iReturn = shmctl(iSysID, cmd, buf);
                if (iReturn < 0)
                {
                    LOG_ERROR("shmctl cmd: %d, err: %s\n", cmd, strerror(errno));
                    va_end(av);
                    return STAT_ERR_SYSTEM_V_SHM_CTRL;
                }
            }
            break;

        case APP_LIB_SYSTEM_V_TYPE_SEM:
            {
                SINT32 semnum = va_arg(av, SINT32);
                SINT32 cmd = va_arg(av, SINT32);
                union semun sembuf = {0};

                if (cmd == SETVAL)
                {
                    sembuf.val = va_arg(av, SINT32);
                }
                else if (cmd == IPC_STAT || cmd == IPC_SET)
                {
                    sembuf.buf = va_arg(av, struct semid_ds *);
                }
                else if (cmd == GETALL || cmd == SETALL)
                {
                    sembuf.array = va_arg(av, unsigned short  *);
                }
                #ifdef __USE_GNU
                else if (cmd == IPC_INFO)
                {
                    sembuf.__buf = va_arg(av, struct seminfo  *);
                }
                #endif

                iReturn = semctl(iSysID, semnum, cmd, sembuf);
                if (iReturn < 0)
                {
                    LOG_ERROR("shmctl semnum: %d,cmd: %d, err: %s\n", semnum, cmd, strerror(errno));
                    va_end(av);
                    return STAT_ERR_SYSTEM_V_SEM_CTRL;
                }
            }
            break;

        default:
            break;
    }

    va_end(av);

    return STAT_SUCCESS;
}

/**********************************************************
 * Function Name:        LibSystem_V_Semop
 * Description:          消息
 * Parameters[in]:       IN SINT32 iSysID
 *                       IN SINT32 iSemNum
 *                       IN LIB_SYSTEM_V_SEM_OP_e opt
 * Parameters[out]:
 * ReturnValue:          ULONG
 * Author:               liuke
 * Date:                 2024.04.16
 * Note:
***********************************************************/
ULONG AppLibSystemV_SemCtrl(IN SINT32 iSysID, IN SINT32 iSemNum, IN APP_LIB_SYSTEM_V_SEM_OP_e opt)
{
    SINT32 iReturn = STAT_SUCCESS;
    struct  sembuf  buf = {0};

    if (iSemNum < 0)
    {
       return  -1;
    }

    buf.sem_num = iSemNum;

    if (opt == APP_LIB_SYSTEM_V_SEM_OP_P || opt == APP_LIB_SYSTEM_V_SEM_OP_V)
    {
        buf.sem_flg = IPC_NOWAIT;
    }
    else if (opt == APP_LIB_SYSTEM_V_SEM_OP_UNDO)
    {
        buf.sem_flg = SEM_UNDO;
    }

    switch (opt)
    {
        case APP_LIB_SYSTEM_V_SEM_OP_P:
        case APP_LIB_SYSTEM_V_SEM_OP_P_BLOCK:
            {
                buf.sem_op = -1;
            }
            break;

        case APP_LIB_SYSTEM_V_SEM_OP_V:
        case APP_LIB_SYSTEM_V_SEM_OP_V_BLOCK:
            {
                buf.sem_op = 1;
            }
            break;

        default:
            break;

    }

    iReturn = semop(iSysID, &buf, 1);
    if (iReturn != 0)
    {
        LOG_ERROR("semop err: %s\n", strerror(errno));
        return STAT_ERR_SYSTEM_V_SEM_OP;
    }

    return STAT_SUCCESS;
}

/**********************************************************
 * Function Name:        LibSystem_V_Shmop
 * Description:          消息
 * Parameters[in]:       IN LIB_SYSTEM_V_TYPE_e type
 * Parameters[out]:
 * ReturnValue:          ULONG
 * Author:               liuke
 * Date:                 2024.04.16
 * Note:
 *     1. AppLibSystemV_Shmop(APP_LIB_SYSTEM_V_SHM_OP_AT, iSysID, <void **>shmaddr);
 *     2. AppLibSystemV_Shmop(APP_LIB_SYSTEM_V_SHM_OP_DT, <void **>shmaddr);
 **********************************************************/
ULONG AppLibSystemV_ShmCtrl(APP_LIB_SYSTEM_V_SHM_OP_e opt, ...)
{
    SINT32 iReturn = STAT_SUCCESS;
    va_list av;

    va_start(av, opt);

    switch (opt)
    {
        case APP_LIB_SYSTEM_V_SHM_OP_AT:
            {
                SINT32 iSysID = va_arg(av, SINT32);
                void *shmaddr = NULL;
                shmaddr = shmat(iSysID, NULL, 0);
                if (shmaddr == (void *) -1)
                {
                    LOG_ERROR("shmat err: %s\n", strerror(errno));
                    va_end(av);
                    return STAT_ERR_SYSTEM_V_SHM_AT;
                }

                void **pshmaddr = va_arg(av, void **);
                *pshmaddr =shmaddr;
            }
            break;

        case APP_LIB_SYSTEM_V_SHM_OP_DT:
            {
                const void *shmaddr = va_arg(av, const void *);
                iReturn = shmdt(shmaddr);
                if (iReturn < 0)
                {
                    LOG_ERROR("shmdt err: %s\n", strerror(errno));
                    va_end(av);
                    return STAT_ERR_SYSTEM_V_SHM_DT;
                }
            }
            break;

        default:
            break;

    }

    va_end(av);

    return STAT_SUCCESS;
}

/**********************************************************
 * Function Name:        libSystem_V_MsgRcv
 * Description:          消息队列读取消息
 * Parameters[in]:       IN SINT32 iQueueId
 *                       INOUT LIB_SYSTEM_V_MSG_s *msg
 *                       IN SINT32 flag
 * Parameters[out]:
 * ReturnValue:          ULONG
 * Author:               liuke
 * Date:                 2024.04.16
 * Note:
***********************************************************/
ULONG AppLibSystemV_MsgRcv(IN SINT32 iQueueId, INOUT APP_LIB_SYSTEM_V_MSG_s *msg, IN SINT32 flag)
{
    SINT32 iLen = 0;

    if ((iQueueId < 0) || (msg == NULL) || (msg->lType < 0))
    {
        LOG_ERROR("msg_type %ld, QueueId %d\n", msg->lType, iQueueId);
        return STAT_ERR_INVAILD_PARAM;
    }

    /* Calculated size */
    iLen = sizeof(*msg) - sizeof(msg->lType) + msg->usLen;

    iLen = msgrcv(iQueueId, msg, iLen, msg->lType, flag);
    if (iLen < 0)
    {
        LOG_ERROR("msgrcv %ld, err: %s\n", msg->lType, strerror(errno));
        return STAT_ERR_SYSTEM_V_MSG_RECV;
    }

    return STAT_SUCCESS;
}

/**********************************************************
 * Function Name:        libSystem_V_MsgRcv
 * Description:          消息队列发送消息
 * Parameters[in]:       IN SINT32 iQueueId
 *                       INOUT LIB_SYSTEM_V_MSG_s *msg
 *                       IN SINT32 flag
 * Parameters[out]:
 * ReturnValue:          ULONG
 * Author:               liuke
 * Date:                 2024.04.16
 * Note:
***********************************************************/
ULONG AppLibSystemV_MsgSnd(IN SINT32 iQueueId, IN APP_LIB_SYSTEM_V_MSG_s *msg, IN SINT32 flag)
{
    SINT32 iLen = 0;

    if ((iQueueId < 0) || (msg == NULL) ||(msg->lType < 0))
    {
        LOG_ERROR("msg_type %ld, QueueId %d\n", msg->lType, iQueueId);
        return STAT_ERR_INVAILD_PARAM;
    }

    /* Calculated size */
    iLen = sizeof(*msg) - sizeof(msg->lType) + msg->usLen;

    iLen = msgsnd(iQueueId, msg, iLen, flag);
    if (iLen < 0)
    {
        LOG_ERROR("msgsnd %ld, err: %s\n", msg->lType, strerror(errno));
        
        if (flag == IPC_NOWAIT && errno == EAGAIN)
        {
            return STAT_ERR_SYSTEM_V_MSG_FULL;
        }

        return STAT_ERR_SYSTEM_V_MSG_SEND;
    }

    return STAT_SUCCESS;
}
